在 Code Review 時,為了維持程式碼可維護性、易擴充原則,Function 的模組化與重用性為重要原則。今天將介紹撰寫程式碼時,開發人員需遵守的撰寫原則,以及 Reviewers 在 Review 時應考量的關鍵方向:
單一職責原則 (Single Responsibility Principle, SRP)
確保一個 Function 只處理單一邏輯,例如一個 Module 負責處理數據的取得,另一個負責將獲取的資料進行渲染,而一個 Function 負責取得數據同時又需要處理業務邏輯或是渲染到畫面上。
// 壞例子:
// 一個函數同時做兩件事情
function fetchDataAndRender() {
const data = fetchData();
render(data);
}
// 好例子:
// 將職責分開
function fetchData() {
// 從API取得數據
return fetch('api/data');
}
function render(data) {
// 渲染數據到畫面上
console.log(data);
}
開放封閉原則 (Open/Closed Principle, OCP)
不更動原功能核心邏輯的情況下,仍易於擴充新需求,例如:增加屬性等。
// 壞例子:
// 若要加入更多動物判斷,就要到 Function 裡調整
function getAnimalSound(animal) {
if (animal === 'dog') {
return 'woof';
} else if (animal === 'cat') {
return 'meow';
}
// 每增加一個新動物都需要修改這裡
}
// 好例子:
// 將動物類別獨立寫在變數裡,可隨時增加動物類型,也不會修改原來的 Function
const animalSounds = {
dog: 'woof',
cat: 'meow'
// 新動物可以直接加在這
};
function getAnimalSound(animal) {
return animalSounds[animal] || 'unknown sound';
}
避免重複原則 (Don’t Repeat Yourself, DRY)
減少重複的程式碼,以便維護和修正時只需修改一處。將重複的邏輯統一寫成可重用的 Function 或 Module。
// 壞例子:有重複程式碼
function greetUser() {
const userName = getUserName();
console.log(`Hello, ${userName}!`);
}
function greetAdmin() {
const adminName = getAdminName();
console.log(`Hello, ${adminName}!`);
}
// 好例子:重用共通函數
function greet(name) {
console.log(`Hello, ${name}!`);
}
greet(getUserName());
greet(getAdminName());
高內聚、低耦合 (High Cohesion and Low Coupling)
高內聚:將相關的屬性和方法集中在一起,以便清晰地表達其功能和職責。
// 在 Article class 中,所有方法都是圍繞著文章的特性設計的,
// 如獲得摘要、新增日期和更新內容,皆與文章的管理有關。
class Article {
constructor(title, content) {
this.title = title;
this.content = content;
this.createdAt = new Date();
}
getSummary() {
return this.content.substring(0, 100) + '...';
}
getCreatedAt() {
return this.createdAt.toLocaleDateString();
}
updateContent(newContent) {
this.content = newContent;
}
}
低耦合:Module 之間應避免過度依賴,降低改動一個 Module 對其他 Module 的影響。
注意:也需考慮到實際的業務功能需求和系統的複雜性。對於簡單的應用,過度地強調高內聚和低耦合可能會導致不必要的複雜性和過度設計。
介面隔離原則 (Interface Segregation Principle, ISP)
讓 Module 只關注自己所需的部分。
// 壞例子:所有物品都要執行不必要的功能
class Item {
use() { /* some code */ }
equip() { /* some code */ } // 某些物品不需要這個功能
}
// 好例子:為不同物品提供不同介面
class UsableItem {
use() { /* some code */ }
}
class EquippableItem extends UsableItem {
equip() { /* some code */ }
}
使用依賴注入 (Dependency Injection, DI)
降低 Module 之間的耦合。
// 壞例子:Function 內部實作依賴
function Logger() {
const fileLogger = new FileLogger();
fileLogger.log('Logging data');
}
// 好例子:透過依賴注入
function Logger(logger) {
logger.log('Logging data');
}
const fileLogger = new FileLogger();
const consoleLogger = new ConsoleLogger();
Logger(fileLogger); // 可以彈性注入不同的logger
Logger(consoleLogger);
重視可重用性 (Reusability)
避免過早優化 (Avoid Premature Optimization)
在設計 Module 時,應著重於程式碼可讀與可維護性,並避免為了微小的效能提升而破壞 Module 結構。
// 壞例子:太早優化,程式碼變得難以閱讀
function getSum(arr) {
let sum = 0, i = 0, n = arr.length;
while (i < n) {
sum += arr[i++];
}
return sum;
}
// 好例子:先保持程式碼清楚,有效能問題再進行優化
function getSum(arr) {
return arr.reduce((total, num) => total + num, 0);
}
遵守一致性和命名規則
高測試覆蓋率 (Testability)
可透過單元測試和集成測試來驗證 Module 的功能,提高系統穩定。
今天提到的模組重用性和 code review 原則強調了 Function 單一職責、高內聚、低耦合以及可測試性等開發設計原則,希望能增加程式碼的可維護性。明天的內容將繼續提供更多實際範例,幫助讀者能較容易將這些原則帶入實際的開發與應用。